package com.binance.basedao.mybatis;

import com.binance.basedao.anotation.*;
import lombok.Getter;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <b>Description:</b>〈自定义Model实体的属性信息〉<br/>
 * @author hongchaoMao <br/>
 *         Create date: 2017/9/18
 * @version v1.0.0
 */
class Property
{
    private static final Logger logger = Logger.getLogger(Property.class.getName());
    @Getter
    private String name;
    
    private String tableName;
    
    private Method readMethod;
    
    private Field field;
    
    private Column column;
    
    Property(Class<?> modelClass, PropertyDescriptor propertyDescriptor)
    {
        name = propertyDescriptor.getName();
        readMethod = propertyDescriptor.getReadMethod();
        
        try {
            field = modelClass.getDeclaredField(propertyDescriptor.getName());
        }
        catch (NoSuchFieldException e1) {
            field = null;
        }
        
        if (isTransient()) {
            return;
        }
        
        // column
        column = (Column) getAnnotation(readMethod, Column.class);
        if (column == null) {
            column = (Column) getAnnotation(field, Column.class);
        }
        
        // tableName
        if (column != null) {
            tableName = column.table();
        }
        if (tableName == null) {
            if (modelClass.getAnnotation(Table.class) == null) {
                String className = modelClass.getName().split("$")[0];
                try {
                    tableName = Class.forName(className).getAnnotation(Table.class).name();
                }
                catch (ClassNotFoundException e) {
                    logger.log(Level.SEVERE, "------>parse() error: ", e);
                }
            }
            else {
                tableName = modelClass.getAnnotation(Table.class).name();
            }
            
        }
    }
    
    boolean isId()
    {
        return hasAnnotation(readMethod, Id.class) || hasAnnotation(field, Id.class);
    }
    
    boolean isOrderColumn()
    {
        return hasAnnotation(readMethod, OrderColumn.class) || hasAnnotation(field, OrderColumn.class);
    }
    
    boolean isTransient()
    {
        return hasAnnotation(readMethod, Transient.class) || hasAnnotation(field, Transient.class);
    }
    
    String getColumnName()
    {
        if (column == null) {
            // 大写字母前插入'_$0'
            return name.replaceAll("([A-Z])", "_$0").toUpperCase();
        }
        else {
            return column.name();
        }
    }
    
    String getOrder()
    {
        OrderColumn orderColumn = (OrderColumn) getAnnotation(readMethod, OrderColumn.class);
        if (orderColumn == null) {
            orderColumn = (OrderColumn) getAnnotation(field, OrderColumn.class);
        }
        return getColumnName() + " " + (orderColumn != null ? orderColumn.name() : "");
    }

    boolean isNullValue(Object object) throws IllegalAccessException, InvocationTargetException
    {
        return readMethod.invoke(object) == null;
    }

    Object getValue(Object object) throws InvocationTargetException, IllegalAccessException
    {
        return readMethod.invoke(object);
    }

    boolean isUnableForColumnTarget(ColumnTarget columnTarget)
    {
        if (column == null) {
            return false;
        }
        
        switch (columnTarget) {
            case INSERT:
                return !column.insertable();
            case UPDATE:
                return !column.updatable();
            default:
                break;
        }
        return false;
    }
    
    private boolean hasAnnotation(AccessibleObject accessibleObject, Class<? extends Annotation> annotationClass)
    {
        return getAnnotation(accessibleObject, annotationClass) != null;
    }
    
    private Annotation getAnnotation(AccessibleObject accessibleObject, Class<? extends Annotation> annotationClass)
    {
        if (accessibleObject == null) {
            return null;
        }
        return accessibleObject.getAnnotation(annotationClass);
    }
    
}
